package com.xuecheng.manage_media.service;


import com.alibaba.fastjson.JSONObject;
import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.response.CheckChunkResult;
import com.xuecheng.framework.domain.media.response.MediaCode;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.manage_media.config.RabbitMQConfig;
import com.xuecheng.manage_media.dao.MediaFileRepository;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.util.*;

@Service
public class MediaUploadService {
    @Resource
    private MediaFileRepository mediaFileRepository;

    @Value("${xc-service-manage-media.video-location}")
    private String upload_location;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Value("${xc-service-manage-media.mq.routingkey-media-video}")
    public String routingKey;

    /**
     * 上传文件第1步->注册文件,检查文件是否存在
     *
     * @param path     文件路径
     * @param fileMd5  文件md5
     * @param fileName 文件名称
     * @param fileSize 文件大小
     * @param mimetype 文件类型
     * @param fileExt  文件扩展名
     * @return 状态
     */
    public ResponseResult register(String path, String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        if (!new File(upload_location + "/" + path).exists()) {
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_PATH_DOES_NOT_EXIST);
        }
        // 检查文件是否在磁盘上存在
        String filePath = upload_location + path + "/" + fileMd5 + "_" + fileName;
        File file = new File(filePath);
        boolean exists = file.isFile();
        // 检查文件的信息在MongoDB中是否存在
        Optional<MediaFile> optional = mediaFileRepository.findById(fileMd5);
        if (exists && optional.isPresent()) {
            // 文件存在了
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_EXIST);
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /**
     * 上传文件第2步->检查分块是否存在
     *
     * @param path      文件路径
     * @param fileMd5   文件md5
     * @param chunk     分块下标
     * @param chunkSize 分块大小
     * @return 结果
     */
    public CheckChunkResult checkChunk(String path, String fileMd5, Integer chunk, Long chunkSize) {
        File file = new File(upload_location + "/" + path + "/" + fileMd5 + "/chunk/" + chunk);
        if (file.exists()) {
            // 块文件存在
            return new CheckChunkResult(CommonCode.SUCCESS, true);
        } else {
            // 块文件不存在
            return new CheckChunkResult(CommonCode.SUCCESS, false);
        }
    }

    /**
     * 上传文件第3步->接收分块文件
     *
     * @param path    文件路径
     * @param file    分块文件
     * @param fileMd5 文件md5
     * @param chunk   分块下标
     * @return 结果
     */
    public ResponseResult uploadChunk(String path, MultipartFile file, String fileMd5, Integer chunk) {
        if (!new File(upload_location + "/" + path).exists()) {
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_PATH_DOES_NOT_EXIST);
        } else {
            // 创建目录接收分块
            File chunkDir = new File(upload_location + "/" + path + "/" + fileMd5 + "/" + "/chunk");
            chunkDir.mkdirs();
            // 创建一个记录文件名字的文件
            new File(upload_location + "/" + path + "/" + fileMd5 + "/" + file.getOriginalFilename()).mkdir();
            // 输入输出流
            InputStream inputStream = null;
            OutputStream outputStream = null;
            try {
                inputStream = file.getInputStream();
                outputStream = new FileOutputStream(new File(chunkDir + "/" + chunk));
                IOUtils.copy(inputStream, outputStream);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /**
     * 上传文件第4步->合并分块
     *
     * @param path     文件路径
     * @param fileMd5  文件md5
     * @param fileName 文件名称
     * @param fileSize 文件大小
     * @param mimetype 文件类型
     * @param fileExt  文件后缀
     * @return 结果
     */
    public ResponseResult mergeChunks(String path, String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        // 得到分块路径和分块文件
        File chunkFile = new File(upload_location + path + "/" + fileMd5 + "/chunk");

        File[] listFiles = chunkFile.listFiles();
        List<File> fileList = Arrays.asList(listFiles);

        // 得到文件名字
        File temp = new File(upload_location + path + "/" + fileMd5);
        File[] files = temp.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                if (name.equals("chunk")) {
                    return false;
                } else {
                    return true;
                }
            }
        });
        String filename = files[0].getName();

        // 得到存储合并文件的文件路径
        File filePath = new File(upload_location + path + "/" + filename);
        File file = this.mergeFile(fileList, filePath);
        if (file == null) {
            return new ResponseResult(CommonCode.FAIL);
        } else {
            // 校验md5
            try {
                FileInputStream fileInputStream = new FileInputStream(file);
                String fileMD5String = DigestUtils.md5DigestAsHex(fileInputStream);
                fileInputStream.close();
                if (fileMd5.equalsIgnoreCase(fileMD5String)) {
                    // 文件合并没问题,开始先MongoDB写信息
                    MediaFile mediaFile = new MediaFile();
                    mediaFile.setFileId(fileMD5String);
                    mediaFile.setFileName(fileMD5String + "." + fileExt);
                    mediaFile.setFileOriginalName(fileName);
                    mediaFile.setFilePath(path);
                    mediaFile.setFileSize(fileSize);
                    mediaFile.setMimeType(mimetype);
                    mediaFile.setFileStatus("301002");
                    mediaFile.setFileType(fileExt);
                    mediaFile.setUploadTime(new Date());
                    mediaFileRepository.save(mediaFile);
                    // 删除分片文件夹
                    FileUtils.deleteDirectory(temp);
                    // 发消息给MQ,通知监听MQ视频队列的服务进行视频分片服务
                    new Thread() {
                        @Override
                        public void run() {
                            Map<String, String> map = new HashMap<>();
                            map.put("mediaId", mediaFile.getFileId());
                            String s = JSONObject.toJSONString(map);
                            rabbitTemplate.convertAndSend(RabbitMQConfig.EX_ROUTING_MEDIA_POSTPAGE, routingKey,s);
                        }
                    }.start();
                    return new ResponseResult(CommonCode.SUCCESS);
                } else {
                    return new ResponseResult(CommonCode.FAIL);
                }
            } catch (IOException e) {
                e.printStackTrace();
                return new ResponseResult(CommonCode.FAIL);
            }
        }
    }

    /**
     * 合并文件的方法
     *
     * @param chunkFileList 需要合并的集合
     * @param mergeFile     合并路径
     * @return 合并好的文件
     */
    private File mergeFile(List<File> chunkFileList, File mergeFile) {
        try {
            //如果合并文件已存在则删除，否则创建新文件
            if (mergeFile.exists()) {
                mergeFile.delete();
            } else {
                //创建一个新文件
                mergeFile.createNewFile();
            }
            //对块文件进行排序
            Collections.sort(chunkFileList, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    if (Integer.parseInt(o1.getName()) > Integer.parseInt(o2.getName())) {
                        return 1;
                    }
                    return -1;
                }
            });

            //创建一个写对象
            RandomAccessFile raf_write = new RandomAccessFile(mergeFile, "rw");
            byte[] b = new byte[1024];
            for (File chunkFile : chunkFileList) {
                RandomAccessFile raf_read = new RandomAccessFile(chunkFile, "r");
                int len = -1;
                while ((len = raf_read.read(b)) != -1) {
                    raf_write.write(b, 0, len);
                }
                raf_read.close();
            }
            raf_write.close();

            return mergeFile;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

